package org.com.fw.web.security;

/**
 * AccessdecisionManager在Spring security中是很重要的。
 * 所有的Authentication实现需要保存在一个GrantedAuthority对象数组中。 
 * 这就是赋予给主体的权限。 GrantedAuthority对象通过AuthenticationManager
 * 保存到 Authentication对象里，然后从AccessDecisionManager读出来，进行授权判断。
 * 
 * 访问url时，会通过AbstractSecurityInterceptor拦截器拦截，
 * 其中会调用FilterInvocationSecurityMetadataSource的方法来获取被拦截url所需的全部权限，在调用授权管理器AccessDecisionManager，
 * 这个授权管理器会通过spring的全局缓存SecurityContextHolder获取用户的权限信息，还会获取被拦截的url和被拦截url所需的全部权限，
 * 然后根据所配的策略（有：一票决定，一票否定，少数服从多数等），如果权限足够，则返回，权限不够则报错并调用权限不足页面.
 *
 */
import java.util.Collection;

import org.com.fw.consts.FwConst;
import org.com.mars.pojo.system.SysUser;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;

public class MarsAccessDecisionManager implements AccessDecisionManager {

	/**
	 * 检查用户是否够权限访问资源，授权访问.
	 * 
	 * @param arg0  从spring的全局缓存SecurityContextHolder中拿到的，里面是用户的权限信息
	 * @param arg1 url
	 * @param arg2 configAttributes所需的权限
	 */
	@Override
	public void decide(Authentication arg0, Object arg1, Collection<ConfigAttribute> arg2)
			throws AccessDeniedException, InsufficientAuthenticationException {
		if (arg2.contains(FwConst.ROLE_CONFIG_ANONYMOUS)) {
			return;
		}
		// 默认公共权限（url权限未定义）
		if (arg2.contains(FwConst.ROLE_CONFIG_PUBLIC)) {
		      return;
		}

		// 用戶没有登录系统
		if (arg0 == null) {
			throw new AccessDeniedException("用戶没有登录系统！");
		}

		// 用戶登陆对象不存在！
		Object principal = arg0.getPrincipal();
		if (principal == null) {
			throw new AccessDeniedException("登录对象为空！");
		}

		// 用户类型判定
		if (!(principal instanceof SysUser)) {
			throw new AccessDeniedException("登录对象必须为SysUser实现类！");
		}

		SysUser user = (SysUser) principal;
		@SuppressWarnings("unchecked")
		Collection<GrantedAuthority> authorities = (Collection<GrantedAuthority>)user.getAuthorities();
		
		// 超级用户权限判定
		if(authorities.contains(FwConst.ROLE_GRANT_SUPER)) {
			return;
		}

		// 权限判定
		for (GrantedAuthority authority : authorities) {
			if (arg2.contains(new SecurityConfig(authority.getAuthority()))) {
				return;
			}
		}
		throw new AccessDeniedException("对不起,你没有访问该页面的权限!");

	}

	@Override
	public boolean supports(ConfigAttribute arg0) {
		return true;
	}

	@Override
	public boolean supports(Class<?> arg0) {
		return true;
	}

}